他のプログラミング言語を経験してからRをいじりだした人に多いと思うのですが、便利関数で簡潔に実現できる処理を、ループ処理で複雑に書いて、苦労した上に見通しとパフォーマンスが悪いコードを生成することがあります。
1 三項演算関数(ifelse
)
C言語の三項演算子に該当する関数があります。
ifelse(c(1, 0) > c(0, 1), "left-side is bigger.", "left-side is not bigger.")
[1] "left-side is bigger." "left-side is not bigger."
2 オブジェクトの保存と読み込み
Rのあらゆる変数は同様に保存と読み込みができます。つまり、ベクトル、行列、配列、リスト、データフレーム、環境、関数、モデル式といったオブジェクトは、saveRDS
で保存して、readRDS
で読み込むことができます。
function(a, b) (a + 1)*(b - 1) # 関数を定義
fnc1 <-saveRDS(fnc1, "fnc.RDS") # 関数を保存
rm(fnc1) # 関数を消す
readRDS("fnc.RDS") # 関数を読み込む
fnc2 <-fnc2(2, 3) # 関数を使う
[1] 6
他のプログラミング言語でも似たようなことはできるのですが、パッケージなしで同じぐらい広範なオブジェクトを同じぐらい容易にできるものはちょっと思い当たりません。
現在のワークスペースのすべてのオブジェクトを保存したい場合はsave.image
を用いることができます。読み込むときはload
を使います。
100
n <- 1:n
x <- runif(n, min = 1, max = 100)
z <- 1 + x - z + rnorm(n, sd = 10)
y <-save.image(file = "example.RData")
ls() # 作成したオブジェクトを確認
[1] "n" "x" "y" "z"
rm(list = ls()) # 前オブジェクト消去
ls() # 空になったのを確認
character(0)
load(file = "example.RData")
ls()
をすると、消したオブジェクトn
,x
,z
,y
が復活しているのが分かります。 なお、quit(save = "yes")
とすると、自動でsave.image
が呼び出されます。
3 分岐(switch
)
Rにもswitch
文がありました。
switch("a", a = 123, b = 456, c = 789)
[1] 123
switch("b", a = 123, b = 456, c = 789)
[1] 456
switch("c", a = 123, b = 456, c = 789)
[1] 789
ベクトル処理ができないので、まとめて処理するときにはunlistとsapplyを組み合わせるしか無いようです。
unlist(sapply(c("a", "b", "c"),
switch, a = 123, b = 456, c = 789))
a b c
123 456 789
4 属性ごとの集計
xtabs
で属性ごとに頻度を数えられますが、aggregate
で属性ごとに平均などを求められます。
aggregate(value ~ type, mean, data = data.frame(
type = letters[round(runif(100, min=0.5, max=2.5))],
value = runif(100)
))
type value
1 a 0.5649560
2 b 0.5604612
なお、第1変数のモデル式はcbind
でつなげて複数列を指定できるので、複数の値を同時に集計できます。
5 ベクトルやデータフレームを分割
男女など属性ごとにベクトルを分けたいときに便利です。
1:9) (x <-
[1] 1 2 3 4 5 6 7 8 9
as.factor(rep(letters[1:3], 3))) (g <-
[1] a b c a b c a b c
Levels: a b c
split(x, g)
$a
[1] 1 4 7
$b
[1] 2 5 8
$c
[1] 3 6 9
左辺のないモデル式でデータフレーム内の変数を指定してデータフレームの分割もでき、リストにして戻してくれます。
# Rに標準添付のIris Datasetを使う
split(iris, ~Species)
lst <-head(lst[[1]])
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
head(lst[[2]])
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
51 7.0 3.2 4.7 1.4 versicolor
52 6.4 3.2 4.5 1.5 versicolor
53 6.9 3.1 4.9 1.5 versicolor
54 5.5 2.3 4.0 1.3 versicolor
55 6.5 2.8 4.6 1.5 versicolor
56 5.7 2.8 4.5 1.3 versicolor
head(lst[[3]])
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
101 6.3 3.3 6.0 2.5 virginica
102 5.8 2.7 5.1 1.9 virginica
103 7.1 3.0 5.9 2.1 virginica
104 6.3 2.9 5.6 1.8 virginica
105 6.5 3.0 5.8 2.2 virginica
106 7.6 3.0 6.6 2.1 virginica
二次元、三次元の分割もモデル式で指定できます。
文字列を分割する場合はstrsplit
を使います。
6 ランクを計算
ベクトル内での昇順の順位を示します。同順の場合の処理はties.method
で指定です。
round(runif(10, min = 1, max = 10))) (x <-
[1] 5 8 7 7 5 5 8 2 5 7
rank(x)
[1] 3.5 9.5 7.0 7.0 3.5 3.5 9.5 1.0 3.5 7.0
rank(x, ties.method = "min")
[1] 2 9 6 6 2 2 9 1 2 6
ゴルフの順位のようにn位タイを計算するのはちょっと手間かもです。
unique(x[duplicated(x)])
ties <-sprintf("%d位%s", rank(x, ties.method = "min"), ifelse(x %in% ties, "タイ", ""))
[1] "2位タイ" "9位タイ" "6位タイ" "6位タイ" "2位タイ" "2位タイ" "9位タイ"
[8] "1位" "2位タイ" "6位タイ"
7 正規表現
Rでも正規表現によるテキスト処理ができます。
c("This is a pen.", "This is an apple.")
txt <-# isが含まれる要素の位置
grep("[^a-z]is[^a-z]", txt)
[1] 1 2
# appleが含まれる要素
grep("apple", txt, value = TRUE)
[1] "This is an apple."
# appleが含まれない要素
grep("apple", txt, value = TRUE, invert = TRUE)
[1] "This is a pen."
# isをwasに置換(1要素に複数あってもすべて置換)
gsub("([^a-z])(is)([^a-z])", "\\1was\\3", txt)
[1] "This was a pen." "This was an apple."
# 空白で分割
strsplit(txt, " ")
[[1]]
[1] "This" "is" "a" "pen."
[[2]]
[1] "This" "is" "an" "apple."
置換時の括弧の中の文字列は\\1
といったエスケープ文字と数字の組み合わせで表現されることに注意しましょう。 なお、括弧自体をマッチングしたい場合は、
gsub("\\(", "括弧始", "(...)")
[1] "括弧始...)"
と言うように括弧の前にエスケープ文字をつけます。
8 \({}_m\!C_n\)
組み合わせの数の計算ができるchoose
関数があります。コンビネーションではないので注意しましょう。
# 5!/(3!2!)
choose(5, 3)
[1] 10
# factorialで書くと長々として遅い
factorial(5)/(factorial(3)*factorial(2))
[1] 10
9 対話モードで動いているか?
interactive()
でTRUE
が戻って来た場合は対話モードで動いています。バッチ処理と対話モードで挙動を変えたいときに使えます。
10 GUIのファイル・ダイアログを使う
使いどころは乏しい気もしますが、file.choose()
でできます。